'use client'; import { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { useToast } from '@/hooks/use-toast'; import { useTranslations } from "next-intl"; import { User, Mail, Calendar, LogOut, Edit, Save, X, Coins, ImageIcon, Plus, Minus, Crown } from 'lucide-react'; import { useAuth } from "@/components/providers" interface UserInfo { id: string; email: string; username: string | null; isEmailVerified: boolean; credits: number; subscriptionCredits: number; subscriptionStatus: string | null; subscriptionPlan: string | null; subscriptionStartDate: string | null; subscriptionEndDate: string | null; } interface CreditActivity { id: string; type: string; description: string; creditAmount: number | null; metadata: string | null; createdAt: string; } interface DashboardContentProps { locale: string; } export default function DashboardContent({ locale }: DashboardContentProps) { const { user, isLoading, refreshUser } = useAuth() const router = useRouter() const t = useTranslations("dashboard") const { toast } = useToast() const tErrors = useTranslations("auth.errors") const tCredit = useTranslations('credit_description') const [isEditing, setIsEditing] = useState(false) const [editUsername, setEditUsername] = useState('') const [isSaving, setIsSaving] = useState(false) const [activities, setActivities] = useState([]) const [isLoadingActivities, setIsLoadingActivities] = useState(true) const [isSubscribing, setIsSubscribing] = useState(false) useEffect(() => { const fetchActivities = async () => { try { const response = await fetch('/api/user/activities?limit=10', { method: 'GET', credentials: 'include', }) if (response.ok) { const data = await response.json() setActivities(data.activities || []) } else { console.error('获取活动记录失败:', response.status) } } catch (error) { console.error('获取活动记录出错:', error) } finally { setIsLoadingActivities(false) } } fetchActivities() }, []) // 格式化时间 const formatDate = (dateString: string) => { const date = new Date(dateString) const now = new Date() const diffTime = Math.abs(now.getTime() - date.getTime()) const diffMinutes = Math.ceil(diffTime / (1000 * 60)) const diffHours = Math.ceil(diffTime / (1000 * 60 * 60)) const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) if (diffMinutes < 60) { return `${diffMinutes} ${locale === 'zh' ? '分钟前' : 'minutes ago'}` } else if (diffHours < 24) { return `${diffHours} ${locale === 'zh' ? '小时前' : 'hours ago'}` } else if (diffDays < 7) { return `${diffDays} ${locale === 'zh' ? '天前' : 'days ago'}` } else { return date.toLocaleDateString(locale === 'zh' ? 'zh-CN' : 'en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }) } } // 获取活动图标 const getActivityIcon = (type: string) => { switch (type) { case 'credit_deduct': return case 'credit_add': return case 'registration_bonus': return case 'image_generation': case 'image_edit': case 'multi_image_edit': return case 'login': return default: return } } // 获取活动类型描述 const getActivityTypeText = (type: string) => { switch (type) { case 'credit_deduct': return t('activityTypes.credit_deduct') case 'credit_add': return t('activityTypes.credit_add') case 'image_generation': case 'image_edit': case 'multi_image_edit': return t('activityTypes.image_generation') case 'login': return t('activityTypes.login') case 'registration_bonus': return t('activityTypes.registration_bonus') default: return t('activityTypes.other') } } // 格式化活动描述 const formatActivityDescription = (activity: CreditActivity) => { const { description } = activity // 如果是翻译键格式 if (description.startsWith('credit_description.')) { const key = description.replace('credit_description.', '') // 处理特殊的图片编辑格式: credit_description.image_edit:具体内容 if (key.startsWith('image_edit:')) { const content = key.replace('image_edit:', '') return `${tCredit('image_edit')}: ${content}` } // 处理多图编辑格式: credit_description.multi_image_edit:具体内容 if (key.startsWith('multi_image_edit:')) { const content = key.replace('multi_image_edit:', '') return `${tCredit('image_edit')}: ${content}` } // 处理多图编辑的默认格式,显示为"图片编辑"而不是"多图编辑" if (key === 'multi_image_edit') { // 尝试从metadata中获取prompt信息 try { const metadata = activity.metadata ? JSON.parse(activity.metadata) : null if (metadata && metadata.prompt) { return `${tCredit('image_edit')}: ${metadata.prompt}` } } catch (e) { // 忽略JSON解析错误 } return tCredit('image_edit') } // 直接翻译键 try { if (key === 'registration_bonus') { return tCredit('registration_bonus') } else if (key === 'background_removal') { return tCredit('background_removal') } else if (key === 'image_edit') { return tCredit('image_edit') } else if (key === 'subscription_activated') { return tCredit('subscription_activated') } else if (key === 'purchase_credits') { return tCredit('purchase_credits') } else if (key === 'subscription_expired') { return tCredit('subscription_expired') } } catch (error) { console.log('Translation not found for key:', key) } } // 兼容旧格式,直接返回描述 return description } const handleEditProfile = () => { setIsEditing(true) } const handleCancelEdit = () => { setIsEditing(false) setEditUsername(user?.username || '') } const handleSaveProfile = async () => { if (!user) return setIsSaving(true) try { const response = await fetch('/api/auth/update-profile', { method: 'PUT', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ username: editUsername.trim() || null, }), }) if (response.ok) { const updatedData = await response.json() refreshUser() setIsEditing(false) // 重新获取活动记录 const fetchActivitiesAgain = async () => { try { const response = await fetch('/api/user/activities?limit=10', { method: 'GET', credentials: 'include', }) if (response.ok) { const data = await response.json() setActivities(data.activities || []) } } catch (error) { console.error('获取活动记录出错:', error) } } fetchActivitiesAgain() toast({ title: t('profileUpdated'), description: t('profileUpdatedDesc'), }) } else { const errorData = await response.json() toast({ title: t('updateFailed'), description: errorData.error || t('updateError'), variant: 'destructive', }) } } catch (error) { toast({ title: t('updateFailed'), description: t('networkError'), variant: 'destructive', }) } finally { setIsSaving(false) } } const handleLogout = async () => { try { const response = await fetch('/api/auth/logout', { method: 'POST', }) if (response.ok) { toast({ title: t('logoutSuccess'), description: t('logoutSuccess'), }) router.push(`/${locale}`) router.refresh() } else { toast({ title: t('logoutFailed'), description: t('logoutError'), variant: 'destructive', }) } } catch (error) { toast({ title: t('logoutFailed'), description: tErrors('networkError'), variant: 'destructive', }) } } const handleSubscribe = async () => { setIsSubscribing(true) try { const response = await fetch('/api/create-checkout-session', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ locale, }), }) const data = await response.json() if (!response.ok) { // 处理翻译键错误消息 const errorMessage = data.error === 'alreadySubscribed' ? '您已有活跃订阅,无需重复订阅' : data.error || '创建支付会话失败' throw new Error(errorMessage) } // 跳转到 Stripe Checkout 页面 if (data.url) { window.location.href = data.url } } catch (error: any) { console.error('Error creating checkout session:', error) toast({ title: '订阅失败', description: error.message || '创建支付会话时发生错误', variant: 'destructive', }) } finally { setIsSubscribing(false) } } // 如果正在加载,显示加载状态 if (isLoading) { return (

{t("loading")}

) } // 如果未登录,重定向到登录页 if (!user) { router.push(`/${locale}/auth/login`) return null } return (

{t('title')}

{t('welcome')},{user?.username || t('user')}!

{t('personalInfo')}
{user?.email}
{user?.username || t('nameNotSet')}
{user?.isEmailVerified ? t('verified') : t('unverified')}
{t('creditBalance')}
{/* 永久积分 */}
{user?.credits || 0}

{t('permanentCredits')}

{/* 订阅积分 */} {user?.subscriptionStatus === 'active' && (
{user?.subscriptionCredits || 0}

{t('subscriptionCredits')}

)} {/* 总积分 */}
{(user?.credits || 0) + (user?.subscriptionCredits || 0)}

{t('totalCredits')}

{t('subscriptionStatus')}
{user?.subscriptionStatus === 'active' ? ( <>
Pro {t('membershipActive')}
{t('expiresAt')}: {user?.subscriptionEndDate ? new Date(user.subscriptionEndDate).toLocaleDateString(locale === 'zh' ? 'zh-CN' : 'en-US') : t('unknown')}
{t('nextBilling')}: {user?.subscriptionEndDate ? new Date(user.subscriptionEndDate).toLocaleDateString(locale === 'zh' ? 'zh-CN' : 'en-US') : t('unknown')}
) : ( <>

{t('freeVersion')}

{t('upgradeToGetMoreFeatures')}

)} {/* 订阅按钮 */}
{t('quickActions')}
{/* 编辑个人资料卡片 */} {isEditing && ( {t('editProfileTitle')} {t('editProfileDesc')}
setEditUsername(e.target.value)} disabled={isSaving} />

{t('emailCannotModify')}

)} {t('recentActivity')} {t('recentActivityDesc')}
{isLoadingActivities ? (

{t('loadingActivities')}

) : activities.length > 0 ? ( activities.map((activity) => (
{getActivityIcon(activity.type)}

{formatActivityDescription(activity)}

{activity.creditAmount && ( 0 ? 'text-green-600' : 'text-red-600' }`}> {activity.creditAmount > 0 ? '+' : ''}{activity.creditAmount} {locale === 'zh' ? '积分' : 'credits'} )}

{getActivityTypeText(activity.type)}

{formatDate(activity.createdAt)}

)) ) : (

{t('noActivities')}

{t('noActivitiesDesc')}

)}
) }